﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;

namespace AcmeDameng.EntityFrameworkCore
{
    //DmSqlNullabilityProcessor
 
    public class FixedDmSqlNullabilityProcessor : SqlNullabilityProcessor
    {
        private readonly List<ColumnExpression> _nonNullableColumns;

        private readonly ISqlExpressionFactory _sqlExpressionFactory;

        protected override bool UseRelationalNulls { get; }

        public FixedDmSqlNullabilityProcessor([NotNull] RelationalParameterBasedSqlProcessorDependencies dependencies, RelationalParameterBasedSqlProcessorParameters parameters)
            : base(dependencies, parameters)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(dependencies, "dependencies");
            _sqlExpressionFactory = dependencies.SqlExpressionFactory;
            UseRelationalNulls = parameters.UseRelationalNulls;
            _nonNullableColumns = new List<ColumnExpression>();
        }

        protected override void AddNonNullableColumn([NotNull] ColumnExpression columnExpression)
        {
            _nonNullableColumns.Add(columnExpression);

            //_nonNullableColumns.Add(Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(columnExpression, "columnExpression"));
        }

        protected override TableExpressionBase Visit([NotNull] TableExpressionBase tableExpressionBase)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(tableExpressionBase, "tableExpressionBase");
            if (!(tableExpressionBase is CrossApplyExpression crossApplyExpression))
            {
                if (!(tableExpressionBase is CrossJoinExpression crossJoinExpression))
                {
                    if (!(tableExpressionBase is ExceptExpression exceptExpression))
                    {
                        if (!(tableExpressionBase is FromSqlExpression result))
                        {
                            if (!(tableExpressionBase is InnerJoinExpression innerJoinExpression))
                            {
                                if (!(tableExpressionBase is IntersectExpression intersectExpression))
                                {
                                    if (!(tableExpressionBase is LeftJoinExpression leftJoinExpression))
                                    {
                                        if (!(tableExpressionBase is OuterApplyExpression outerApplyExpression))
                                        {
                                            if (!(tableExpressionBase is SelectExpression selectExpression))
                                            {
                                                if (!(tableExpressionBase is TableValuedFunctionExpression tableValuedFunctionExpression))
                                                {
                                                    if (!(tableExpressionBase is TableExpression result2))
                                                    {
                                                        if (tableExpressionBase is UnionExpression unionExpression)
                                                        {
                                                            SelectExpression source = Visit(unionExpression.Source1);
                                                            SelectExpression source2 = Visit(unionExpression.Source2);
                                                            return unionExpression.Update(source, source2);
                                                        }

                                                        throw new InvalidOperationException(RelationalStrings.UnhandledExpressionInVisitor(tableExpressionBase, tableExpressionBase.GetType(), "SqlNullabilityProcessor"));
                                                    }

                                                    return result2;
                                                }

                                                List<SqlExpression> list = new List<SqlExpression>();
                                                foreach (SqlExpression argument in tableValuedFunctionExpression.Arguments)
                                                {
                                                    list.Add(Visit(argument, out var _));
                                                }

                                                return tableValuedFunctionExpression.Update(list);
                                            }

                                            return Visit(selectExpression);
                                        }

                                        return outerApplyExpression.Update(Visit(outerApplyExpression.Table));
                                    }

                                    TableExpressionBase table = Visit(leftJoinExpression.Table);
                                    SqlExpression joinPredicate = ProcessJoinPredicate(leftJoinExpression.JoinPredicate);
                                    return leftJoinExpression.Update(table, joinPredicate);
                                }

                                SelectExpression source3 = Visit(intersectExpression.Source1);
                                SelectExpression source4 = Visit(intersectExpression.Source2);
                                return intersectExpression.Update(source3, source4);
                            }

                            TableExpressionBase table2 = Visit(innerJoinExpression.Table);
                            SqlExpression sqlExpression = ProcessJoinPredicate(innerJoinExpression.JoinPredicate);
                            if (!TryGetBoolConstantValue(sqlExpression).GetValueOrDefault())
                            {
                                return innerJoinExpression.Update(table2, sqlExpression);
                            }

                            return new CrossJoinExpression(table2);
                        }

                        return result;
                    }

                    SelectExpression source5 = Visit(exceptExpression.Source1);
                    SelectExpression source6 = Visit(exceptExpression.Source2);
                    return exceptExpression.Update(source5, source6);
                }

                return crossJoinExpression.Update(Visit(crossJoinExpression.Table));
            }

            return crossApplyExpression.Update(Visit(crossApplyExpression.Table));
        }

        protected override SelectExpression Visit([NotNull] SelectExpression selectExpression)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(selectExpression, "selectExpression");
            bool flag = false;
            List<ProjectionExpression> list = (List<ProjectionExpression>)selectExpression.Projection;
            bool nullable;
            for (int i = 0; i < selectExpression.Projection.Count; i++)
            {
                ProjectionExpression projectionExpression = selectExpression.Projection[i];
                ProjectionExpression projectionExpression2 = projectionExpression.Update(Visit(projectionExpression.Expression, out nullable));
                if (projectionExpression2 != projectionExpression && list == selectExpression.Projection)
                {
                    list = new List<ProjectionExpression>();
                    for (int j = 0; j < i; j++)
                    {
                        list.Add(selectExpression.Projection[j]);
                    }

                    flag = true;
                }

                if (list != selectExpression.Projection)
                {
                    list.Add(projectionExpression2);
                }
            }

            List<TableExpressionBase> list2 = (List<TableExpressionBase>)selectExpression.Tables;
            for (int k = 0; k < selectExpression.Tables.Count; k++)
            {
                TableExpressionBase tableExpressionBase = selectExpression.Tables[k];
                TableExpressionBase tableExpressionBase2 = Visit(tableExpressionBase);
                if (tableExpressionBase2 != tableExpressionBase && list2 == selectExpression.Tables)
                {
                    list2 = new List<TableExpressionBase>();
                    for (int l = 0; l < k; l++)
                    {
                        list2.Add(selectExpression.Tables[l]);
                    }

                    flag = true;
                }

                if (list2 != selectExpression.Tables)
                {
                    list2.Add(tableExpressionBase2);
                }
            }

            SqlExpression sqlExpression = Visit(selectExpression.Predicate, allowOptimizedExpansion: true, out nullable);
            flag |= sqlExpression != selectExpression.Predicate;
            if (TryGetBoolConstantValue(sqlExpression).GetValueOrDefault())
            {
                sqlExpression = null;
                flag = true;
            }

            List<SqlExpression> list3 = (List<SqlExpression>)selectExpression.GroupBy;
            for (int m = 0; m < selectExpression.GroupBy.Count; m++)
            {
                SqlExpression sqlExpression2 = selectExpression.GroupBy[m];
                SqlExpression sqlExpression3 = Visit(sqlExpression2, out nullable);
                if (sqlExpression3 != sqlExpression2 && list3 == selectExpression.GroupBy)
                {
                    list3 = new List<SqlExpression>();
                    for (int n = 0; n < m; n++)
                    {
                        list3.Add(selectExpression.GroupBy[n]);
                    }

                    flag = true;
                }

                if (list3 != selectExpression.GroupBy)
                {
                    list3.Add(sqlExpression3);
                }
            }

            SqlExpression sqlExpression4 = Visit(selectExpression.Having, allowOptimizedExpansion: true, out nullable);
            flag |= sqlExpression4 != selectExpression.Having;
            if (TryGetBoolConstantValue(sqlExpression4).GetValueOrDefault())
            {
                sqlExpression4 = null;
                flag = true;
            }

            List<OrderingExpression> list4 = (List<OrderingExpression>)selectExpression.Orderings;
            for (int num = 0; num < selectExpression.Orderings.Count; num++)
            {
                OrderingExpression orderingExpression = selectExpression.Orderings[num];
                OrderingExpression orderingExpression2 = orderingExpression.Update(Visit(orderingExpression.Expression, out nullable));
                if (orderingExpression2 != orderingExpression && list4 == selectExpression.Orderings)
                {
                    list4 = new List<OrderingExpression>();
                    for (int num2 = 0; num2 < num; num2++)
                    {
                        list4.Add(selectExpression.Orderings[num2]);
                    }

                    flag = true;
                }

                if (list4 != selectExpression.Orderings)
                {
                    list4.Add(orderingExpression2);
                }
            }

            SqlExpression sqlExpression5 = Visit(selectExpression.Offset, out nullable);
            flag |= sqlExpression5 != selectExpression.Offset;
            SqlExpression sqlExpression6 = Visit(selectExpression.Limit, out nullable);
            if (!(flag | (sqlExpression6 != selectExpression.Limit)))
            {
                return selectExpression;
            }

            return selectExpression.Update(list2, sqlExpression, list3, sqlExpression4, list, list4, sqlExpression5, sqlExpression6);
        }

        protected override SqlExpression? Visit(SqlExpression? sqlExpression, out bool nullable)
        {
            return Visit(sqlExpression, allowOptimizedExpansion: false, out nullable);
        }

        protected override SqlExpression? Visit(SqlExpression? sqlExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            return Visit(sqlExpression, allowOptimizedExpansion, preserveNonNullableColumns: false, out nullable);
        }

        private SqlExpression? Visit(SqlExpression? sqlExpression, bool allowOptimizedExpansion, bool preserveNonNullableColumns, out bool nullable)
        {
            if (sqlExpression == null)
            {
                nullable = false;
                return sqlExpression;
            }

            int count = _nonNullableColumns.Count;
            SqlExpression result = ((sqlExpression is CaseExpression caseExpression) ? VisitCase(caseExpression, allowOptimizedExpansion, out nullable) : ((sqlExpression is CollateExpression collateExpression) ? VisitCollate(collateExpression, allowOptimizedExpansion, out nullable) : ((sqlExpression is ColumnExpression columnExpression) ? VisitColumn(columnExpression, allowOptimizedExpansion, out nullable) : ((sqlExpression is DistinctExpression distinctExpression) ? VisitDistinct(distinctExpression, allowOptimizedExpansion, out nullable) : ((sqlExpression is ExistsExpression existsExpression) ? VisitExists(existsExpression, allowOptimizedExpansion, out nullable) : ((sqlExpression is InExpression inExpression) ? VisitIn(inExpression, allowOptimizedExpansion, out nullable) : ((sqlExpression is LikeExpression likeExpression) ? VisitLike(likeExpression, allowOptimizedExpansion, out nullable) : ((sqlExpression is RowNumberExpression rowNumberExpression) ? VisitRowNumber(rowNumberExpression, allowOptimizedExpansion, out nullable) : ((sqlExpression is ScalarSubqueryExpression scalarSubqueryExpression) ? VisitScalarSubquery(scalarSubqueryExpression, allowOptimizedExpansion, out nullable) : ((sqlExpression is SqlBinaryExpression sqlBinaryExpression) ? VisitSqlBinary(sqlBinaryExpression, allowOptimizedExpansion, out nullable) : ((sqlExpression is SqlConstantExpression sqlConstantExpression) ? VisitSqlConstant(sqlConstantExpression, allowOptimizedExpansion, out nullable) : ((sqlExpression is SqlFragmentExpression sqlFragmentExpression) ? VisitSqlFragment(sqlFragmentExpression, allowOptimizedExpansion, out nullable) : ((sqlExpression is SqlFunctionExpression sqlFunctionExpression) ? VisitSqlFunction(sqlFunctionExpression, allowOptimizedExpansion, out nullable) : ((sqlExpression is SqlParameterExpression sqlParameterExpression) ? VisitSqlParameter(sqlParameterExpression, allowOptimizedExpansion, out nullable) : ((!(sqlExpression is SqlUnaryExpression sqlUnaryExpression)) ? VisitCustomSqlExpression(sqlExpression, allowOptimizedExpansion, out nullable) : VisitSqlUnary(sqlUnaryExpression, allowOptimizedExpansion, out nullable))))))))))))))));
            if (!preserveNonNullableColumns)
            {
                RestoreNonNullableColumnsList(count);
            }

            return result;
        }

        protected override SqlExpression VisitCustomSqlExpression([NotNull] SqlExpression sqlExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            throw new InvalidOperationException(RelationalStrings.UnhandledExpressionInVisitor(sqlExpression, sqlExpression.GetType(), "SqlNullabilityProcessor"));
        }

        protected override SqlExpression VisitCase([NotNull] CaseExpression caseExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(caseExpression, "caseExpression");
            nullable = caseExpression.ElseResult == null;
            int count = _nonNullableColumns.Count;
            bool nullable2;
            SqlExpression operand = Visit(caseExpression.Operand, out nullable2);
            List<CaseWhenClause> list = new List<CaseWhenClause>();
            bool allowOptimizedExpansion2 = caseExpression.Operand == null;
            bool flag = false;
            foreach (CaseWhenClause whenClause in caseExpression.WhenClauses)
            {
                SqlExpression sqlExpression = Visit(whenClause.Test, allowOptimizedExpansion2, preserveNonNullableColumns: true, out nullable2);
                bool? flag2 = TryGetBoolConstantValue(sqlExpression);
                if (flag2.HasValue)
                {
                    if (!flag2.GetValueOrDefault())
                    {
                        RestoreNonNullableColumnsList(count);
                        continue;
                    }

                    flag = true;
                }

                bool nullable3;
                SqlExpression result = Visit(whenClause.Result, out nullable3);
                nullable |= nullable3;
                list.Add(new CaseWhenClause(sqlExpression, result));
                RestoreNonNullableColumnsList(count);
                if (flag)
                {
                    break;
                }
            }

            SqlExpression sqlExpression2 = null;
            if (!flag)
            {
                sqlExpression2 = Visit(caseExpression.ElseResult, out var nullable4);
                nullable |= nullable4;
            }

            if (list.Count == 0)
            {
                return sqlExpression2 ?? _sqlExpressionFactory.Constant(null, caseExpression.TypeMapping);
            }

            if (sqlExpression2 != null || list.Count != 1 || !TryGetBoolConstantValue(list[0].Test).GetValueOrDefault())
            {
                return caseExpression.Update(operand, list, sqlExpression2);
            }

            return list[0].Result;
        }

        protected override SqlExpression VisitCollate([NotNull] CollateExpression collateExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(collateExpression, "collateExpression");
            return collateExpression.Update(Visit(collateExpression.Operand, out nullable));
        }

        protected override SqlExpression VisitColumn([NotNull] ColumnExpression columnExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(columnExpression, "columnExpression");
            nullable = columnExpression.IsNullable && !_nonNullableColumns.Contains(columnExpression);
            return columnExpression;
        }

        protected override SqlExpression VisitDistinct([NotNull] DistinctExpression distinctExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(distinctExpression, "distinctExpression");
            return distinctExpression.Update(Visit(distinctExpression.Operand, out nullable));
        }

        protected override SqlExpression VisitExists([NotNull] ExistsExpression existsExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(existsExpression, "existsExpression");
            SelectExpression selectExpression = Visit(existsExpression.Subquery);
            nullable = false;
            if (TryGetBoolConstantValue(selectExpression.Predicate) != false)
            {
                return existsExpression.Update(selectExpression);
            }

            return selectExpression.Predicate;
        }

        protected override SqlExpression VisitIn(InExpression inExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            SqlExpression sqlExpression = base.VisitIn(inExpression, allowOptimizedExpansion, out nullable);
            if (sqlExpression is SqlConstantExpression sqlConstantExpression && sqlConstantExpression.Type == typeof(bool))
            {
                return _sqlExpressionFactory.Equal(_sqlExpressionFactory.Constant(true, inExpression.TypeMapping), sqlConstantExpression);
            }

            return sqlExpression;
        }

        protected override SqlExpression VisitLike([NotNull] LikeExpression likeExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(likeExpression, "likeExpression");
            bool nullable2;
            SqlExpression match = Visit(likeExpression.Match, out nullable2);
            bool nullable3;
            SqlExpression pattern = Visit(likeExpression.Pattern, out nullable3);
            bool nullable4;
            SqlExpression escapeChar = Visit(likeExpression.EscapeChar, out nullable4);
            nullable = nullable2 || nullable3 || nullable4;
            return likeExpression.Update(match, pattern, escapeChar);
        }

        protected override SqlExpression VisitRowNumber([NotNull] RowNumberExpression rowNumberExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(rowNumberExpression, "rowNumberExpression");
            bool flag = false;
            List<SqlExpression> list = new List<SqlExpression>();
            bool nullable2;
            foreach (SqlExpression partition in rowNumberExpression.Partitions)
            {
                SqlExpression sqlExpression = Visit(partition, out nullable2);
                flag = flag || sqlExpression != partition;
                list.Add(sqlExpression);
            }

            List<OrderingExpression> list2 = new List<OrderingExpression>();
            foreach (OrderingExpression ordering in rowNumberExpression.Orderings)
            {
                OrderingExpression orderingExpression = ordering.Update(Visit(ordering.Expression, out nullable2));
                flag = flag || orderingExpression != ordering;
                list2.Add(orderingExpression);
            }

            nullable = false;
            if (!flag)
            {
                return rowNumberExpression;
            }

            return rowNumberExpression.Update(list, list2);
        }

        protected override SqlExpression VisitScalarSubquery([NotNull] ScalarSubqueryExpression scalarSubqueryExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(scalarSubqueryExpression, "scalarSubqueryExpression");
            nullable = true;
            return scalarSubqueryExpression.Update(Visit(scalarSubqueryExpression.Subquery));
        }

        protected override SqlExpression VisitSqlBinary([NotNull] SqlBinaryExpression sqlBinaryExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(sqlBinaryExpression, "sqlBinaryExpression");
            bool optimize = allowOptimizedExpansion;
            allowOptimizedExpansion = allowOptimizedExpansion && (sqlBinaryExpression.OperatorType == ExpressionType.AndAlso || sqlBinaryExpression.OperatorType == ExpressionType.OrElse);
            int count = _nonNullableColumns.Count;
            bool nullable2;
            SqlExpression sqlExpression = Visit(sqlBinaryExpression.Left, allowOptimizedExpansion, preserveNonNullableColumns: true, out nullable2);
            List<ColumnExpression> first = _nonNullableColumns.Skip(count).ToList();
            if (sqlBinaryExpression.OperatorType != ExpressionType.AndAlso)
            {
                RestoreNonNullableColumnsList(count);
            }

            bool nullable3;
            SqlExpression sqlExpression2 = Visit(sqlBinaryExpression.Right, allowOptimizedExpansion, preserveNonNullableColumns: true, out nullable3);
            if (sqlBinaryExpression.OperatorType == ExpressionType.OrElse)
            {
                List<ColumnExpression> collection = first.Intersect(_nonNullableColumns.Skip(count)).ToList();
                RestoreNonNullableColumnsList(count);
                _nonNullableColumns.AddRange(collection);
            }
            else if (sqlBinaryExpression.OperatorType != ExpressionType.AndAlso)
            {
                RestoreNonNullableColumnsList(count);
            }

            if (sqlBinaryExpression.OperatorType == ExpressionType.Add && sqlBinaryExpression.Type == typeof(string))
            {
                if (nullable2)
                {
                    sqlExpression = AddNullConcatenationProtection(sqlExpression, sqlBinaryExpression.TypeMapping);
                }

                if (nullable3)
                {
                    sqlExpression2 = AddNullConcatenationProtection(sqlExpression2, sqlBinaryExpression.TypeMapping);
                }

                nullable = false;
                return sqlBinaryExpression.Update(sqlExpression, sqlExpression2);
            }

            if (sqlBinaryExpression.OperatorType == ExpressionType.Equal || sqlBinaryExpression.OperatorType == ExpressionType.NotEqual)
            {
                SqlBinaryExpression sqlBinaryExpression2 = sqlBinaryExpression.Update(sqlExpression, sqlExpression2);
                SqlExpression sqlExpression3 = OptimizeComparison(sqlBinaryExpression2, sqlExpression, sqlExpression2, nullable2, nullable3, out nullable);
                if (sqlExpression3 is SqlUnaryExpression sqlUnaryExpression && sqlUnaryExpression.OperatorType == ExpressionType.NotEqual && sqlUnaryExpression.Operand is ColumnExpression item)
                {
                    _nonNullableColumns.Add(item);
                }

                if (sqlExpression3.Equals(sqlBinaryExpression2) && (nullable2 || nullable3) && !UseRelationalNulls)
                {
                    return RewriteNullSemantics(sqlBinaryExpression2, sqlBinaryExpression2.Left, sqlBinaryExpression2.Right, nullable2, nullable3, optimize, out nullable);
                }

                return sqlExpression3;
            }

            nullable = nullable2 || nullable3;
            SqlBinaryExpression sqlBinaryExpression3 = sqlBinaryExpression.Update(sqlExpression, sqlExpression2);
            if (sqlBinaryExpression3 != null)
            {
                SqlBinaryExpression sqlExpression4 = sqlBinaryExpression3;
                if (sqlBinaryExpression.OperatorType == ExpressionType.AndAlso || sqlBinaryExpression.OperatorType == ExpressionType.OrElse)
                {
                    return SimplifyLogicalSqlBinaryExpression(sqlExpression4);
                }
            }

            return sqlBinaryExpression3;
            SqlExpression AddNullConcatenationProtection(SqlExpression argument, RelationalTypeMapping typeMapping)
            {
                if (!(argument is SqlConstantExpression) && !(argument is SqlParameterExpression))
                {
                    return _sqlExpressionFactory.Coalesce(argument, _sqlExpressionFactory.Constant(string.Empty, typeMapping));
                }

                return _sqlExpressionFactory.Constant(string.Empty, typeMapping);
            }
        }

        protected override SqlExpression VisitSqlConstant([NotNull] SqlConstantExpression sqlConstantExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(sqlConstantExpression, "sqlConstantExpression");
            nullable = sqlConstantExpression.Value == null;
            return sqlConstantExpression;
        }

        protected override SqlExpression VisitSqlFragment([NotNull] SqlFragmentExpression sqlFragmentExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(sqlFragmentExpression, "sqlFragmentExpression");
            nullable = false;
            return sqlFragmentExpression;
        }

        protected override SqlExpression VisitSqlFunction([NotNull] SqlFunctionExpression sqlFunctionExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(sqlFunctionExpression, "sqlFunctionExpression");
            if (sqlFunctionExpression.IsBuiltIn && sqlFunctionExpression.Arguments != null && string.Equals(sqlFunctionExpression.Name, "COALESCE", StringComparison.OrdinalIgnoreCase))
            {
                bool nullable2;
                SqlExpression sqlExpression = Visit(sqlFunctionExpression.Arguments[0], out nullable2);
                bool nullable3;
                SqlExpression sqlExpression2 = Visit(sqlFunctionExpression.Arguments[1], out nullable3);
                nullable = nullable2 && nullable3;
                return sqlFunctionExpression.Update(sqlFunctionExpression.Instance, new SqlExpression[2] { sqlExpression, sqlExpression2 });
            }

            bool nullable4;
            SqlExpression instance = Visit(sqlFunctionExpression.Instance, out nullable4);
            nullable = sqlFunctionExpression.IsNullable;
            if (sqlFunctionExpression.IsNiladic)
            {
                return sqlFunctionExpression.Update(instance, sqlFunctionExpression.Arguments);
            }

            SqlExpression[] array = new SqlExpression[sqlFunctionExpression.Arguments.Count];
            for (int i = 0; i < array.Length; i++)
            {
                array[i] = Visit(sqlFunctionExpression.Arguments[i], out nullable4);
            }

            if (!sqlFunctionExpression.IsBuiltIn || !string.Equals(sqlFunctionExpression.Name, "SUM", StringComparison.OrdinalIgnoreCase))
            {
                return sqlFunctionExpression.Update(instance, array);
            }

            return _sqlExpressionFactory.Coalesce(sqlFunctionExpression.Update(instance, array), _sqlExpressionFactory.Constant(0, sqlFunctionExpression.TypeMapping), sqlFunctionExpression.TypeMapping);
        }

        protected override SqlExpression VisitSqlParameter([NotNull] SqlParameterExpression sqlParameterExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(sqlParameterExpression, "sqlParameterExpression");
            nullable = ParameterValues[sqlParameterExpression.Name] == null;
            if (!nullable)
            {
                return sqlParameterExpression;
            }

            return _sqlExpressionFactory.Constant(null, sqlParameterExpression.TypeMapping);
        }

        protected override SqlExpression VisitSqlUnary([NotNull] SqlUnaryExpression sqlUnaryExpression, bool allowOptimizedExpansion, out bool nullable)
        {
            //Microsoft.EntityFrameworkCore.Utilities.Check.NotNull(sqlUnaryExpression, "sqlUnaryExpression");
            bool nullable2;
            SqlExpression operand = Visit(sqlUnaryExpression.Operand, out nullable2);
            SqlUnaryExpression sqlUnaryExpression2 = sqlUnaryExpression.Update(operand);
            if (sqlUnaryExpression.OperatorType == ExpressionType.Equal || sqlUnaryExpression.OperatorType == ExpressionType.NotEqual)
            {
                SqlExpression sqlExpression = ProcessNullNotNull(sqlUnaryExpression2, nullable2);
                nullable = false;
                if (sqlExpression is SqlUnaryExpression sqlUnaryExpression3 && sqlUnaryExpression3.OperatorType == ExpressionType.NotEqual && sqlUnaryExpression3.Operand is ColumnExpression item)
                {
                    _nonNullableColumns.Add(item);
                }

                return sqlExpression;
            }

            nullable = nullable2;
            if (nullable2 || sqlUnaryExpression.OperatorType != ExpressionType.Not)
            {
                return sqlUnaryExpression2;
            }

            return OptimizeNonNullableNotExpression(sqlUnaryExpression2);
        }

        private static bool? TryGetBoolConstantValue(SqlExpression? expression)
        {
            if (expression is SqlConstantExpression sqlConstantExpression)
            {
                object value = sqlConstantExpression.Value;
                if (value is bool)
                {
                    return (bool)value;
                }
            }

            return null;
        }

        private void RestoreNonNullableColumnsList(int counter)
        {
            if (counter < _nonNullableColumns.Count)
            {
                _nonNullableColumns.RemoveRange(counter, _nonNullableColumns.Count - counter);
            }
        }

        private SqlExpression ProcessJoinPredicate(SqlExpression predicate)
        {
            if (predicate is SqlBinaryExpression sqlBinaryExpression)
            {
                bool nullable3;
                if (sqlBinaryExpression.OperatorType == ExpressionType.Equal)
                {
                    bool nullable;
                    SqlExpression left = Visit(sqlBinaryExpression.Left, allowOptimizedExpansion: true, out nullable);
                    bool nullable2;
                    SqlExpression right = Visit(sqlBinaryExpression.Right, allowOptimizedExpansion: true, out nullable2);
                    return OptimizeComparison(sqlBinaryExpression.Update(left, right), left, right, nullable, nullable2, out nullable3);
                }

                if (sqlBinaryExpression.OperatorType == ExpressionType.AndAlso || sqlBinaryExpression.OperatorType == ExpressionType.NotEqual || sqlBinaryExpression.OperatorType == ExpressionType.GreaterThan || sqlBinaryExpression.OperatorType == ExpressionType.GreaterThanOrEqual || sqlBinaryExpression.OperatorType == ExpressionType.LessThan || sqlBinaryExpression.OperatorType == ExpressionType.LessThanOrEqual)
                {
                    return Visit(sqlBinaryExpression, allowOptimizedExpansion: true, out nullable3);
                }
            }

            throw new InvalidOperationException(RelationalStrings.UnhandledExpressionInVisitor(predicate, predicate.GetType(), "SqlNullabilityProcessor"));
        }

        private SqlExpression OptimizeComparison(SqlBinaryExpression sqlBinaryExpression, SqlExpression left, SqlExpression right, bool leftNullable, bool rightNullable, out bool nullable)
        {
            bool flag = leftNullable && (left is SqlConstantExpression || left is SqlParameterExpression);
            if (rightNullable && (right is SqlConstantExpression || right is SqlParameterExpression))
            {
                SqlExpression result = ((sqlBinaryExpression.OperatorType == ExpressionType.Equal) ? ProcessNullNotNull(_sqlExpressionFactory.IsNull(left), leftNullable) : ProcessNullNotNull(_sqlExpressionFactory.IsNotNull(left), leftNullable));
                nullable = false;
                return result;
            }

            if (flag)
            {
                SqlExpression result2 = ((sqlBinaryExpression.OperatorType == ExpressionType.Equal) ? ProcessNullNotNull(_sqlExpressionFactory.IsNull(right), rightNullable) : ProcessNullNotNull(_sqlExpressionFactory.IsNotNull(right), rightNullable));
                nullable = false;
                return result2;
            }

            if (!leftNullable && left.Equals(right))
            {
                nullable = false;
                return _sqlExpressionFactory.Equal(_sqlExpressionFactory.Constant(true), _sqlExpressionFactory.Constant(sqlBinaryExpression.OperatorType == ExpressionType.Equal, sqlBinaryExpression.TypeMapping));
            }

            if (!leftNullable && !rightNullable && (sqlBinaryExpression.OperatorType == ExpressionType.Equal || sqlBinaryExpression.OperatorType == ExpressionType.NotEqual))
            {
                SqlUnaryExpression sqlUnaryExpression = left as SqlUnaryExpression;
                SqlUnaryExpression sqlUnaryExpression2 = right as SqlUnaryExpression;
                bool flag2 = IsLogicalNot(sqlUnaryExpression);
                bool flag3 = IsLogicalNot(sqlUnaryExpression2);
                if (flag2)
                {
                    left = sqlUnaryExpression.Operand;
                }

                if (flag3)
                {
                    right = sqlUnaryExpression2.Operand;
                }

                nullable = false;
                if (!((sqlBinaryExpression.OperatorType == ExpressionType.Equal) ^ (flag2 == flag3)))
                {
                    return _sqlExpressionFactory.Equal(left, right);
                }

                return _sqlExpressionFactory.NotEqual(left, right);
            }

            nullable = false;
            return sqlBinaryExpression.Update(left, right);
        }

        private SqlExpression RewriteNullSemantics(SqlBinaryExpression sqlBinaryExpression, SqlExpression left, SqlExpression right, bool leftNullable, bool rightNullable, bool optimize, out bool nullable)
        {
            SqlUnaryExpression sqlUnaryExpression = left as SqlUnaryExpression;
            SqlUnaryExpression sqlUnaryExpression2 = right as SqlUnaryExpression;
            bool flag = IsLogicalNot(sqlUnaryExpression);
            bool flag2 = IsLogicalNot(sqlUnaryExpression2);
            if (flag)
            {
                left = sqlUnaryExpression.Operand;
            }

            if (flag2)
            {
                right = sqlUnaryExpression2.Operand;
            }

            SqlExpression sqlExpression = ProcessNullNotNull(_sqlExpressionFactory.IsNull(left), leftNullable);
            SqlExpression leftIsNotNull = OptimizeNonNullableNotExpression(_sqlExpressionFactory.Not(sqlExpression));
            SqlExpression sqlExpression2 = ProcessNullNotNull(_sqlExpressionFactory.IsNull(right), rightNullable);
            SqlExpression sqlExpression3 = OptimizeNonNullableNotExpression(_sqlExpressionFactory.Not(sqlExpression2));
            if (optimize && sqlBinaryExpression.OperatorType == ExpressionType.Equal && !flag && !flag2)
            {
                if (leftNullable && rightNullable)
                {
                    nullable = true;
                    return SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.OrElse(_sqlExpressionFactory.Equal(left, right), SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.AndAlso(sqlExpression, sqlExpression2))));
                }

                if ((leftNullable && !rightNullable) || (!leftNullable && rightNullable))
                {
                    nullable = true;
                    return _sqlExpressionFactory.Equal(left, right);
                }
            }

            nullable = false;
            if (sqlBinaryExpression.OperatorType == ExpressionType.Equal)
            {
                if (leftNullable && rightNullable)
                {
                    if (flag != flag2)
                    {
                        return ExpandNegatedNullableEqualNullable(left, right, sqlExpression, leftIsNotNull, sqlExpression2, sqlExpression3);
                    }

                    return ExpandNullableEqualNullable(left, right, sqlExpression, leftIsNotNull, sqlExpression2, sqlExpression3);
                }

                if (leftNullable && !rightNullable)
                {
                    if (flag != flag2)
                    {
                        return ExpandNegatedNullableEqualNonNullable(left, right, leftIsNotNull);
                    }

                    return ExpandNullableEqualNonNullable(left, right, leftIsNotNull);
                }

                if (rightNullable && !leftNullable)
                {
                    if (flag != flag2)
                    {
                        return ExpandNegatedNullableEqualNonNullable(left, right, sqlExpression3);
                    }

                    return ExpandNullableEqualNonNullable(left, right, sqlExpression3);
                }
            }

            if (sqlBinaryExpression.OperatorType == ExpressionType.NotEqual)
            {
                if (leftNullable && rightNullable)
                {
                    if (flag != flag2)
                    {
                        return ExpandNegatedNullableNotEqualNullable(left, right, sqlExpression, leftIsNotNull, sqlExpression2, sqlExpression3);
                    }

                    return ExpandNullableNotEqualNullable(left, right, sqlExpression, leftIsNotNull, sqlExpression2, sqlExpression3);
                }

                if (leftNullable && !rightNullable)
                {
                    if (flag != flag2)
                    {
                        return ExpandNegatedNullableNotEqualNonNullable(left, right, sqlExpression);
                    }

                    return ExpandNullableNotEqualNonNullable(left, right, sqlExpression);
                }

                if (rightNullable && !leftNullable)
                {
                    if (flag != flag2)
                    {
                        return ExpandNegatedNullableNotEqualNonNullable(left, right, sqlExpression2);
                    }

                    return ExpandNullableNotEqualNonNullable(left, right, sqlExpression2);
                }
            }

            return sqlBinaryExpression.Update(left, right);
        }

        private SqlExpression SimplifyLogicalSqlBinaryExpression(SqlExpression sqlExpression)
        {
            SqlBinaryExpression sqlBinaryExpression = sqlExpression as SqlBinaryExpression;
            if (sqlBinaryExpression.Left is SqlUnaryExpression sqlUnaryExpression && sqlBinaryExpression.Right is SqlUnaryExpression sqlUnaryExpression2 && (sqlUnaryExpression.OperatorType == ExpressionType.Equal || sqlUnaryExpression.OperatorType == ExpressionType.NotEqual) && (sqlUnaryExpression2.OperatorType == ExpressionType.Equal || sqlUnaryExpression2.OperatorType == ExpressionType.NotEqual) && sqlUnaryExpression.Operand.Equals(sqlUnaryExpression2.Operand))
            {
                if (sqlUnaryExpression.OperatorType != sqlUnaryExpression2.OperatorType)
                {
                    return _sqlExpressionFactory.Constant(sqlBinaryExpression.OperatorType == ExpressionType.OrElse, sqlBinaryExpression.TypeMapping);
                }

                return sqlUnaryExpression;
            }

            if (sqlBinaryExpression.Left is SqlConstantExpression sqlConstantExpression && sqlConstantExpression.Value is bool flag)
            {
                if (sqlBinaryExpression.OperatorType != ExpressionType.AndAlso)
                {
                    if (!flag)
                    {
                        return sqlBinaryExpression.Right;
                    }

                    return sqlConstantExpression;
                }

                if (!flag)
                {
                    return sqlConstantExpression;
                }

                return sqlBinaryExpression.Right;
            }

            if (sqlBinaryExpression.Right is SqlConstantExpression sqlConstantExpression2 && sqlConstantExpression2.Value is bool flag2)
            {
                if (sqlBinaryExpression.OperatorType != ExpressionType.AndAlso)
                {
                    if (!flag2)
                    {
                        return sqlBinaryExpression.Left;
                    }

                    return sqlConstantExpression2;
                }

                if (!flag2)
                {
                    return sqlConstantExpression2;
                }

                return sqlBinaryExpression.Left;
            }

            return sqlBinaryExpression;
        }

        protected SqlExpression OptimizeNonNullableNotExpression(SqlExpression sqlExpression)
        {
            SqlUnaryExpression sqlUnaryExpression = sqlExpression as SqlUnaryExpression;
            if (sqlUnaryExpression.OperatorType != ExpressionType.Not)
            {
                return sqlUnaryExpression;
            }

            SqlExpression operand = sqlUnaryExpression.Operand;
            if (!(operand is SqlConstantExpression sqlConstantExpression))
            {
                if (!(operand is SqlUnaryExpression sqlUnaryExpression2))
                {
                    if (operand is SqlBinaryExpression sqlBinaryExpression)
                    {
                        if (sqlBinaryExpression.OperatorType == ExpressionType.AndAlso || sqlBinaryExpression.OperatorType == ExpressionType.OrElse)
                        {
                            SqlExpression left = OptimizeNonNullableNotExpression(_sqlExpressionFactory.Not(sqlBinaryExpression.Left));
                            SqlExpression right = OptimizeNonNullableNotExpression(_sqlExpressionFactory.Not(sqlBinaryExpression.Right));
                            return SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.MakeBinary((sqlBinaryExpression.OperatorType == ExpressionType.AndAlso) ? ExpressionType.OrElse : ExpressionType.AndAlso, left, right, sqlBinaryExpression.TypeMapping));
                        }

                        if (sqlBinaryExpression.OperatorType == ExpressionType.Equal)
                        {
                            if (sqlBinaryExpression.Left is SqlConstantExpression sqlConstantExpression2 && sqlConstantExpression2.Type == typeof(bool))
                            {
                                return _sqlExpressionFactory.MakeBinary(ExpressionType.Equal, _sqlExpressionFactory.Constant(!(bool)sqlConstantExpression2.Value, sqlConstantExpression2.TypeMapping), sqlBinaryExpression.Right, sqlBinaryExpression.TypeMapping);
                            }

                            if (sqlBinaryExpression.Right is SqlConstantExpression sqlConstantExpression3 && sqlConstantExpression3.Type == typeof(bool))
                            {
                                return _sqlExpressionFactory.MakeBinary(ExpressionType.Equal, sqlBinaryExpression.Left, _sqlExpressionFactory.Constant(!(bool)sqlConstantExpression3.Value, sqlConstantExpression3.TypeMapping), sqlBinaryExpression.TypeMapping);
                            }
                        }

                        if (TryNegate(sqlBinaryExpression.OperatorType, out var result2))
                        {
                            return _sqlExpressionFactory.MakeBinary(result2, sqlBinaryExpression.Left, sqlBinaryExpression.Right, sqlBinaryExpression.TypeMapping);
                        }
                    }
                }
                else
                {
                    switch (sqlUnaryExpression2.OperatorType)
                    {
                        case ExpressionType.Not:
                            return sqlUnaryExpression2.Operand;
                        case ExpressionType.Equal:
                            return _sqlExpressionFactory.IsNotNull(sqlUnaryExpression2.Operand);
                        case ExpressionType.NotEqual:
                            return _sqlExpressionFactory.IsNull(sqlUnaryExpression2.Operand);
                    }
                }
            }
            else if (sqlConstantExpression.Value is bool flag)
            {
                return _sqlExpressionFactory.Constant(!flag, sqlUnaryExpression.TypeMapping);
            }

            return sqlUnaryExpression;
            static bool TryNegate(ExpressionType expressionType, out ExpressionType result)
            {
                ExpressionType? expressionType2 = expressionType switch
                {
                    ExpressionType.Equal => ExpressionType.NotEqual,
                    ExpressionType.NotEqual => ExpressionType.Equal,
                    ExpressionType.GreaterThan => ExpressionType.LessThanOrEqual,
                    ExpressionType.GreaterThanOrEqual => ExpressionType.LessThan,
                    ExpressionType.LessThan => ExpressionType.GreaterThanOrEqual,
                    ExpressionType.LessThanOrEqual => ExpressionType.GreaterThan,
                    _ => null,
                };
                result = expressionType2.GetValueOrDefault();
                return expressionType2.HasValue;
            }
        }

        private SqlExpression ProcessNullNotNull(SqlExpression sqlExpression, bool operandNullable)
        {
            SqlUnaryExpression sqlUnaryExpression = sqlExpression as SqlUnaryExpression;
            if (sqlUnaryExpression == null)
            {
                return sqlExpression;
            }

            if (!operandNullable)
            {
                return _sqlExpressionFactory.Equal(_sqlExpressionFactory.Constant(true), _sqlExpressionFactory.Constant(sqlUnaryExpression.OperatorType == ExpressionType.NotEqual, sqlUnaryExpression.TypeMapping));
            }

            SqlExpression operand = sqlUnaryExpression.Operand;
            if (!(operand is SqlConstantExpression sqlConstantExpression))
            {
                if (!(operand is SqlParameterExpression sqlParameterExpression))
                {
                    if (!(operand is ColumnExpression columnExpression))
                    {
                        if (!(operand is SqlUnaryExpression sqlUnaryExpression2))
                        {
                            if (!(operand is SqlBinaryExpression sqlBinaryExpression))
                            {
                                if (operand is SqlFunctionExpression sqlFunctionExpression)
                                {
                                    if (sqlFunctionExpression.IsBuiltIn && string.Equals("COALESCE", sqlFunctionExpression.Name, StringComparison.OrdinalIgnoreCase))
                                    {
                                        SqlExpression left = ProcessNullNotNull(_sqlExpressionFactory.MakeUnary(sqlUnaryExpression.OperatorType, sqlFunctionExpression.Arguments[0], typeof(bool), sqlUnaryExpression.TypeMapping), operandNullable);
                                        SqlExpression right = ProcessNullNotNull(_sqlExpressionFactory.MakeUnary(sqlUnaryExpression.OperatorType, sqlFunctionExpression.Arguments[1], typeof(bool), sqlUnaryExpression.TypeMapping), operandNullable);
                                        return SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.MakeBinary((sqlUnaryExpression.OperatorType == ExpressionType.Equal) ? ExpressionType.AndAlso : ExpressionType.OrElse, left, right, sqlUnaryExpression.TypeMapping));
                                    }

                                    if (!sqlFunctionExpression.IsNullable)
                                    {
                                        return _sqlExpressionFactory.Equal(_sqlExpressionFactory.Constant(true), _sqlExpressionFactory.Constant(sqlUnaryExpression.OperatorType == ExpressionType.NotEqual, sqlUnaryExpression.TypeMapping));
                                    }

                                    List<SqlExpression> list = new List<SqlExpression>();
                                    if (sqlFunctionExpression.Instance != null && sqlFunctionExpression.InstancePropagatesNullability.GetValueOrDefault())
                                    {
                                        list.Add(sqlFunctionExpression.Instance);
                                    }

                                    if (!sqlFunctionExpression.IsNiladic)
                                    {
                                        for (int i = 0; i < sqlFunctionExpression.Arguments.Count; i++)
                                        {
                                            if (sqlFunctionExpression.ArgumentsPropagateNullability[i])
                                            {
                                                list.Add(sqlFunctionExpression.Arguments[i]);
                                            }
                                        }
                                    }

                                    if (list.Count > 0)
                                    {
                                        return list.Select((SqlExpression e) => ProcessNullNotNull(_sqlExpressionFactory.MakeUnary(sqlUnaryExpression.OperatorType, e, sqlUnaryExpression.Type, sqlUnaryExpression.TypeMapping), operandNullable)).Aggregate((SqlExpression r, SqlExpression e) => SimplifyLogicalSqlBinaryExpression((sqlUnaryExpression.OperatorType == ExpressionType.Equal) ? _sqlExpressionFactory.OrElse(r, e) : _sqlExpressionFactory.AndAlso(r, e)));
                                    }
                                }
                            }
                            else if (sqlBinaryExpression.OperatorType != ExpressionType.AndAlso && sqlBinaryExpression.OperatorType != ExpressionType.OrElse)
                            {
                                SqlExpression left2 = ProcessNullNotNull(_sqlExpressionFactory.MakeUnary(sqlUnaryExpression.OperatorType, sqlBinaryExpression.Left, typeof(bool), sqlUnaryExpression.TypeMapping), operandNullable);
                                SqlExpression right2 = ProcessNullNotNull(_sqlExpressionFactory.MakeUnary(sqlUnaryExpression.OperatorType, sqlBinaryExpression.Right, typeof(bool), sqlUnaryExpression.TypeMapping), operandNullable);
                                return SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.MakeBinary((sqlUnaryExpression.OperatorType == ExpressionType.Equal) ? ExpressionType.OrElse : ExpressionType.AndAlso, left2, right2, sqlUnaryExpression.TypeMapping));
                            }
                        }
                        else
                        {
                            switch (sqlUnaryExpression2.OperatorType)
                            {
                                case ExpressionType.Convert:
                                case ExpressionType.Negate:
                                case ExpressionType.Not:
                                    return ProcessNullNotNull(sqlUnaryExpression.Update(sqlUnaryExpression2.Operand), operandNullable);
                                case ExpressionType.Equal:
                                case ExpressionType.NotEqual:
                                    return _sqlExpressionFactory.Equal(_sqlExpressionFactory.Constant(true), _sqlExpressionFactory.Constant(sqlUnaryExpression2.OperatorType == ExpressionType.NotEqual, sqlUnaryExpression2.TypeMapping));
                            }
                        }
                    }
                    else if (!columnExpression.IsNullable || _nonNullableColumns.Contains(columnExpression))
                    {
                        return _sqlExpressionFactory.Equal(_sqlExpressionFactory.Constant(true), _sqlExpressionFactory.Constant(sqlUnaryExpression.OperatorType == ExpressionType.NotEqual, sqlUnaryExpression.TypeMapping));
                    }

                    return sqlUnaryExpression;
                }

                return _sqlExpressionFactory.Equal(_sqlExpressionFactory.Constant(true), _sqlExpressionFactory.Constant((ParameterValues[sqlParameterExpression.Name] == null) ^ (sqlUnaryExpression.OperatorType == ExpressionType.NotEqual), sqlUnaryExpression.TypeMapping));
            }

            return _sqlExpressionFactory.Equal(_sqlExpressionFactory.Constant(true), _sqlExpressionFactory.Constant((sqlConstantExpression.Value == null) ^ (sqlUnaryExpression.OperatorType == ExpressionType.NotEqual), sqlUnaryExpression.TypeMapping));
        }

        private static bool IsLogicalNot(SqlUnaryExpression? sqlUnaryExpression)
        {
            if (sqlUnaryExpression != null && sqlUnaryExpression.OperatorType == ExpressionType.Not)
            {
                return sqlUnaryExpression.Type == typeof(bool);
            }

            return false;
        }

        private SqlExpression ExpandNullableEqualNullable(SqlExpression left, SqlExpression right, SqlExpression leftIsNull, SqlExpression leftIsNotNull, SqlExpression rightIsNull, SqlExpression rightIsNotNull)
        {
            return SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.OrElse(SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.AndAlso(_sqlExpressionFactory.Equal(left, right), SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.AndAlso(leftIsNotNull, rightIsNotNull)))), SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.AndAlso(leftIsNull, rightIsNull))));
        }

        private SqlExpression ExpandNegatedNullableEqualNullable(SqlExpression left, SqlExpression right, SqlExpression leftIsNull, SqlExpression leftIsNotNull, SqlExpression rightIsNull, SqlExpression rightIsNotNull)
        {
            return SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.OrElse(SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.AndAlso(_sqlExpressionFactory.NotEqual(left, right), SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.AndAlso(leftIsNotNull, rightIsNotNull)))), SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.AndAlso(leftIsNull, rightIsNull))));
        }

        private SqlExpression ExpandNullableEqualNonNullable(SqlExpression left, SqlExpression right, SqlExpression leftIsNotNull)
        {
            return SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.AndAlso(_sqlExpressionFactory.Equal(left, right), leftIsNotNull));
        }

        private SqlExpression ExpandNegatedNullableEqualNonNullable(SqlExpression left, SqlExpression right, SqlExpression leftIsNotNull)
        {
            return SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.AndAlso(_sqlExpressionFactory.NotEqual(left, right), leftIsNotNull));
        }

        private SqlExpression ExpandNullableNotEqualNullable(SqlExpression left, SqlExpression right, SqlExpression leftIsNull, SqlExpression leftIsNotNull, SqlExpression rightIsNull, SqlExpression rightIsNotNull)
        {
            return SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.AndAlso(SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.OrElse(_sqlExpressionFactory.NotEqual(left, right), SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.OrElse(leftIsNull, rightIsNull)))), SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.OrElse(leftIsNotNull, rightIsNotNull))));
        }

        private SqlExpression ExpandNegatedNullableNotEqualNullable(SqlExpression left, SqlExpression right, SqlExpression leftIsNull, SqlExpression leftIsNotNull, SqlExpression rightIsNull, SqlExpression rightIsNotNull)
        {
            return SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.AndAlso(SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.OrElse(_sqlExpressionFactory.Equal(left, right), SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.OrElse(leftIsNull, rightIsNull)))), SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.OrElse(leftIsNotNull, rightIsNotNull))));
        }

        private SqlExpression ExpandNullableNotEqualNonNullable(SqlExpression left, SqlExpression right, SqlExpression leftIsNull)
        {
            return SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.OrElse(_sqlExpressionFactory.NotEqual(left, right), leftIsNull));
        }

        private SqlExpression ExpandNegatedNullableNotEqualNonNullable(SqlExpression left, SqlExpression right, SqlExpression leftIsNull)
        {
            return SimplifyLogicalSqlBinaryExpression(_sqlExpressionFactory.OrElse(_sqlExpressionFactory.Equal(left, right), leftIsNull));
        }
    }

}
